using System;

using FineCollection.Const;
using FineCollection.Intf;




namespace FineCollection.Cont
{
    public class ArrList<T> : ArrBaseCollection<T>, WRandomList<T>
    {

        /// <summary>
        /// Constructs an empty array-based list.
        /// </summary>
        /// <param name="firstIndex">origin index (index of the first item).</param>
        public ArrList(int firstIndex) 
            : base(firstIndex)
        {}


        /// <summary>
        /// Constructs an empty array-based list with the specified capacity.
        /// </summary>
        /// <param name="firstIndex">origin index (index of the first item).</param>
        /// <param name="cap">neccessary capacity.</param>
        public ArrList(int firstIndex, int cap)
            : base(firstIndex, cap)
        {}


        /// <summary>
        /// Adds the specified item to the end of the list.
        /// </summary>
        /// <param name="item">item to add.</param>
        public void Add(T item)
        {
            InternalAddItem(item);
        }


        /// <summary>
        /// Adds all items from the specified array-based collection.
        /// </summary>
        /// <param name="collection">collection to copy items from.</param>
        public void AddAll(ArrBaseCollection<T> collection)
        {
            int newN = collection.count;
            if (newN > 0)
            {
                EnsureCapacity(count + newN);
                Array.Copy(collection.ar, 0, this.ar, count, newN);
                count += newN; lastIndex += newN;
            }
        }


        /// <summary>
        /// Adds all items from the specified constant collection.
        /// </summary>
        /// <param name="collection">collection to copy items from.</param>
        public void AddAll(ConstArray<T> collection)
        {
            int newN = collection.count;
            if (newN > 0)
            {
                EnsureCapacity(count + newN);
                Array.Copy(collection.arr, 0, this.ar, count, newN);
                count += newN; lastIndex += newN;
            }
        }


        /// <summary>
        /// Adds all items from the specified constant collection.
        /// </summary>
        /// <param name="collection">collection to copy items from.</param>
        public void AddAll(RCollection<T> collection)
        {
            if (collection.IsNotEmpty)
            {
                EnsureCapacity(this.Count + collection.Count);
                var it = collection.TakeIterator();
                while (it.Next())
                    Add(it.Current);
            }
        }


        /// <summary>
        /// Represents a quick writeable indexed access to the specified item.
        /// </summary>
        /// <remarks>
        /// The given index must be in [ FirstIndex .. LastIndex ] inclusive.
        /// </remarks>
        /// <param name="index">index of item to access.</param>
        /// <exception cref="CollectionIsEmptyException">when the collection is empty.</exception>
        /// <exception cref="IndexOutOfBoundsException">when index is incorrect.</exception>
        /// <seealso cref="RRandomList{T}.FirstIndex"/>
        /// <seealso cref="RRandomList{T}.LastIndex"/>
        public new T this[int index]
        {
            get 
            {           
                int intIndex = CalcInternalIndex(index);
                return ar[intIndex];
            }

            set
            {
                int intIndex = CalcInternalIndex(index);
                ar[intIndex] = value;
            }
        }


        ///<summary>
        /// Inserts the specified item into the specified position.
        ///</summary>
        /// <remarks>
        /// After the operation completed, 
        /// the specified item will be at the specified postion.
        /// Items were at this and next positions will be shifted right.
        /// </remarks>
        ///<param name="index"></param>
        ///<param name="item"></param>
        public void IncludeAt(int index, T item)
        {
            this.InsertElement(index, item);
        }


        /// <summary>
        /// Excludes the first item from the list.
        /// </summary>
        /// <returns>just excluded item.</returns>
        /// <remarks>The complexity of the operation is O(n).</remarks>
        /// <exception cref="CollectionIsEmptyException">when the list has no items.</exception>
        public T ExcludeFirst()
        {
            if (IsEmpty)
                throw new CollectionIsEmptyException("Attempted to exclude first item from an empty array-based collection.");

            return ExcludeAt(firstIndex);
        }


        /// <summary>
        /// Excludes the specified by index item from the collection and returns it.
        /// </summary>
        /// <param name="index">index of item to exclude.</param>
        /// <returns>just excluded item.</returns>
        public T ExcludeAt(int index)
        {
            T theItem = this[index];
            this.RemoveElementAt(index);
            return theItem;
        }


        /// <summary>
        /// Turns itself into a constant list. 
        /// The resulting list takes all items of this array list,
        /// and this array list detaches from all items.
        /// This operation is highly optimized; 
        /// it performs in a constant time independent of number of items.
        /// </summary>
        /// <returns>Constant list of this items. 
        ///          The first and last index are the same of this list ones.</returns>
        public ConstList<T> TurnIntoConst()
        {
            // in case of empty array just create a new empty const list and return it
            if (count == 0)
                return new ConstZeroList<T>(firstIndex);

            // if too many empty space we decide to shrink the array before making it constant
            int over = ar.Length - count;
            if (over > 8 && over >= count/2)
                Shrink();

            // prepare the result constant,
            // since this just created constant gets ownership of owr internal array
            var result = new ConstList<T>(firstIndex, ar, count, true);

            // detach from our internal array because rhe 'result' will take care of it
            ar = null;
            count = 0;
            lastIndex = firstIndex - 1;

            // ok
            return result;
        }
    }
}
